use crate::db::dao;
use crate::{controller, db, http, mq, service};
use serde::{Deserialize, Serialize};
use std::sync::Arc;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Config {
    pub http: HttpConfig,
    pub redis: crate::redis::Config,
    pub mysql: db::Config,
    pub mqtt: mq::client::Config,
    pub log: wanmesh_common::log::Config,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct HttpConfig {
    pub server: http::server::Config,
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("{0}")]
    DbError(#[from] db::DbErr),
    #[error("{0}")]
    Other(#[from] anyhow::Error),
}

pub struct App {
    config: Config,
}

impl App {
    pub fn new(config: Config) -> Self {
        Self { config }
    }

    pub async fn run(&self) -> Result<(), anyhow::Error> {
        let log_setting = wanmesh_common::log::Setting::try_from(&self.config.log)?;
        wanmesh_common::log::init(log_setting);

        let redis_conn = crate::redis::open(&self.config.redis).await?;

        let db = db::connect(&self.config.mysql).await?;

        let mq_setting = mq::client::Setting::try_from(&self.config.mqtt)?;
        let mq_client_id = new_mq_client_id()?;
        let mq_client_factory = Arc::new(mq::client::Factory::new(mq_setting.clone()));
        let (mq_client, mut mq_event_loop_task) =
            mq::client::Client::new(mq_setting, mq_client_id)?;
        let mq_client_set = Arc::new(mq::client::ClientSet::new(Arc::new(mq_client)));

        let dao_set = dao::DaoSet::new(db);
        let service_set = service::ServiceSet::new(dao_set, redis_conn);
        let service_set = Arc::new(service_set);
        let controller_set = controller::ControllerSet::new(service_set, mq_client_set.clone());
        let http_server = http::server::Server::new(
            self.config.http.server.clone(),
            Arc::new(controller_set),
            mq_client_factory,
        );

        let mq_event_loop_handle = tokio::spawn(async move { mq_event_loop_task.run().await });

        let http_server_handle = tokio::spawn(async move { http_server.run().await });

        tokio::select! {
            _ = mq_event_loop_handle => {
                log::error!("mq事件循环处理异常结束");
            }
            http_server_task_result = http_server_handle => {
                http_server_task_result.map_err(|e| anyhow::Error::new(e))??;
            }
        };

        log::error!("应用异常结束");
        Ok(())
    }
}

pub fn new_mq_client_id() -> Result<String, anyhow::Error> {
    let mac = mac_address::get_mac_address()
        .map_err(|e| anyhow::Error::new(e))?
        .ok_or(anyhow::anyhow!("未获取到MAC地址"))?;
    Ok(format!("wanmesh-server-{}", hex::encode(mac.bytes())))
}
